{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rwxGnsA92emp"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "CPII1rGR2rF9"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JtEZ1pCPn--z"
      },
      "source": [
        "# Индивидуальное обучение: пошаговое руководство"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GV1F7tVTN3Dn"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/customization/custom_training_walkthrough\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/tutorials/customization/custom_training_walkthrough.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/tutorials/customization/custom_training_walkthrough.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/tutorials/customization/custom_training_walkthrough.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8a2322303a3f"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LDrzLFXE8T1l"
      },
      "source": [
        "В этом руководстве используется машинное обучение для *классификации* цветов ириса по видам. Мы используем TensorFlow для:\n",
        "1. Построения модели.\n",
        "2. Обучения этой модели на примерах данных.\n",
        "3. Использование модели, для создания прогноза на неизвестных данных.\n",
        "\n",
        "## Программирование c TensorFlow\n",
        "\n",
        "В этом руководстве используются следующие общие концепции TensorFlow:\n",
        "\n",
        "* Используйте TensorFlow [eager execution](../../guide/eager.ipynb) ,\n",
        "* Импортируйте данные с помощью [Datasets API](../../guide/datasets.ipynb),\n",
        "* Создавайте модели и слои с помощью [Keras API](../../guide/keras/overview.ipynb).\n",
        "\n",
        "Это руководство построено так же, как и многие программы TensorFlow:\n",
        "\n",
        "1. Импортируйте и проанализируйте набор данных.\n",
        "2. Выберите тип модели.\n",
        "3. Обучите модель.\n",
        "4. Оцените эффективность модели.\n",
        "5. Используйте обученную модель, чтобы делать прогнозы."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yNr7H-AIoLOR"
      },
      "source": [
        "## Установка"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1J3AuPBT9gyR"
      },
      "source": [
        "### Настройка импорта\n",
        "\n",
        "Импортируйте TensorFlow и другие необходимые модули Python. По умолчанию TensorFlow использует [активное выполнение](../../guide/eager.ipynb) для немедленного вычисления операций, возвращая конкретные значения вместо создания вычислительного графа, который выполняется позже. Если вы привыкли к REPL или интерактивной консоли python, это примерно тоже."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jElLULrDhQZR"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "import matplotlib.pyplot as plt"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bfV2Dai0Ow2o"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "g4Wzg69bnwK2"
      },
      "outputs": [],
      "source": [
        "print(\"TensorFlow version: {}\".format(tf.__version__))\n",
        "print(\"Eager execution: {}\".format(tf.executing_eagerly()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Zx7wc0LuuxaJ"
      },
      "source": [
        "## Проблема классификации цветов ириса\n",
        "\n",
        "Представьте, что вы ботаник, ищущий автоматизированный способ классифицировать каждый найденный цветок ириса. Машинное обучение предоставляет множество алгоритмов для статистической классификации цветов. Например, сложная программа машинного обучения может классифицировать цветы на основе фотографий. Наши амбиции более скромные - мы собираемся классифицировать цветы ириса на основе размеров их [чашелистиков](https://en.wikipedia.org/wiki/Sepal) и [лепестков](https://en.wikipedia.org/wiki/Petal).\n",
        "\n",
        "Род Iris включает около 300 видов, но наша программа классифицирует только следующие три:\n",
        "\n",
        "* Iris setosa\n",
        "* Iris virginica\n",
        "* Iris versicolor\n",
        "\n",
        "<table>\n",
        "  <tr><td>\n",
        "    <img src=\"https://www.tensorflow.org/images/iris_three_species.jpg\"\n",
        "         alt=\"Petal geometry compared for three iris species: Iris setosa, Iris virginica, and Iris versicolor\">\n",
        "  </td></tr>\n",
        "  <tr><td align=\"center\">\n",
        "    <b>Figure 1.</b> <a href=\"https://commons.wikimedia.org/w/index.php?curid=170298\">Iris setosa</a> (by <a href=\"https://commons.wikimedia.org/wiki/User:Radomil\">Radomil</a>, CC BY-SA 3.0), <a href=\"https://commons.wikimedia.org/w/index.php?curid=248095\">Iris versicolor</a>, (by <a href=\"https://commons.wikimedia.org/wiki/User:Dlanglois\">Dlanglois</a>, CC BY-SA 3.0), and <a href=\"https://www.flickr.com/photos/33397993@N05/3352169862\">Iris virginica</a> (by <a href=\"https://www.flickr.com/photos/33397993@N05\">Frank Mayfield</a>, CC BY-SA 2.0).<br/>&nbsp;\n",
        "  </td></tr>\n",
        "</table>\n",
        "\n",
        "К счастью, кто-то уже создал [набор данных из 120 цветов ириса](https://en.wikipedia.org/wiki/Iris_flower_data_set) с размерами чашелистиков и лепестков. Это классический набор данных, популярный при решении задач классификации машинного обучения для начинающих."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3Px6KAg0Jowz"
      },
      "source": [
        "## Импорт и анализ тренировочного датасета\n",
        "\n",
        "Загрузите файл и преобразуйте его в структуру, которая может использоваться программой Python.\n",
        "\n",
        "### Загрузка датасета\n",
        "\n",
        "Загрузите файл тренировочного датасета с помощью функции tf.keras.utils.get_file. Этот метод возвращает путь к загруженному файлу:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "J6c7uEU9rjRM"
      },
      "outputs": [],
      "source": [
        "train_dataset_url = \"https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\"\n",
        "\n",
        "train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),\n",
        "                                           origin=train_dataset_url)\n",
        "\n",
        "print(\"Локальная копия датасета: {}\".format(train_dataset_fp))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qnX1-aLors4S"
      },
      "source": [
        "### Проверка данных\n",
        "\n",
        "Этот датасет, `iris_training.csv`, представляет собой простой текстовый файл, в котором хранятся табличные данные, отформатированные как значения, разделенные запятыми (CSV). Используйте команду `head -n5`, чтобы взглянуть на первые пять записей:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FQvb_JYdrpPm"
      },
      "outputs": [],
      "source": [
        "!head -n5 {train_dataset_fp}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kQhzD6P-uBoq"
      },
      "source": [
        "Обратите внимание на следующее:\n",
        "\n",
        "1. Первая строка - это заголовок, содержащий информацию о датасете:\n",
        "   * Всего 120 примеров. В каждом примере есть четыре признака и один из трех возможных ярлыков.\n",
        "2. Последующие строки представляют собой записи данных, по одной *[пример](https://developers.google.com/machine-learning/glossary/#example)* на строку, где:\n",
        "   * Первые четыре поля - это *[признаки](https://developers.google.com/machine-learning/glossary/#feature)* примера. Здесь поля содержат числа с плавающей запятой, представляющие размеры цветов.\n",
        "   * Последний столбец - это *[ярлык](https://developers.google.com/machine-learning/glossary/#label)*: это значение, которое мы хотим предсказать. Для этого набора данных это целое число 0, 1 или 2, соответствующее названию цветка.\n",
        "\n",
        "Запишем это в коде:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9Edhevw7exl6"
      },
      "outputs": [],
      "source": [
        "# столбцы в CSV файле\n",
        "column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']\n",
        "\n",
        "feature_names = column_names[:-1]\n",
        "label_name = column_names[-1]\n",
        "\n",
        "print(\"Признаки: {}\".format(feature_names))\n",
        "print(\"Ярлык: {}\".format(label_name))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CCtwLoJhhDNc"
      },
      "source": [
        "Каждый ярлык связана с именем строки(например, «сетоса»), но машинное обучение обычно основывается на числовых значениях. Номера ярлыков соответствуют именам, например:\n",
        "\n",
        "* `0`: Ирис сетоса\n",
        "* `1`: Ирис разноцветный\n",
        "* `2`: Ирис виргинский\n",
        "\n",
        "Дополнительные сведения о функциях и ярлыках см. В разделе [Терминология машинного обучения](https://developers.google.com/machine-learning/crash-course/framing/ml-terminology)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sVNlJlUOhkoX"
      },
      "outputs": [],
      "source": [
        "class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dqPkQExM2Pwt"
      },
      "source": [
        "### Создание объекта `tf.data.Dataset`\n",
        "\n",
        "TensorFlow [Dataset API](../../guide/data.ipynb) обрабатывает наиболее распространенные случаи загрузки данных в модель. Это API высокого уровня для чтения данных и преобразования их в форму, используемую для обучения.\n",
        "\n",
        "Поскольку набор данных представляет собой текстовый файл в формате CSV, используйте функцию `tf.data.experimental.make_csv_dataset`, чтобы прочитать csv файл и преобразовать данные в нужный формат. Так как эта функция генерирует данные для моделей, которые используются для изучения ML, ее поведение по умолчанию - перемешать данные (`shuffle = True, shuffle_buffer_size = 10000`) и бесконечно повторять пакеты(` num_epochs = None`). Мы также устанавливаем размер пакета [batch_size](https://developers.google.com/machine-learning/glossary/#batch_size):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WsxHnz1ebJ2S"
      },
      "outputs": [],
      "source": [
        "batch_size = 32\n",
        "\n",
        "train_dataset = tf.data.experimental.make_csv_dataset(\n",
        "    train_dataset_fp,\n",
        "    batch_size,\n",
        "    column_names=column_names,\n",
        "    label_name=label_name,\n",
        "    num_epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gB_RSn62c-3G"
      },
      "source": [
        "Функция `make_csv_dataset` возвращает объект `tf.data.Dataset`, содержащий пары `(features, label)` , где `features` - словарь: `{'feature_name': value}`\n",
        "\n",
        "Объекты `Dataset` итерируемые. Давайте посмотрим на набор признаков:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iDuG94H-C122"
      },
      "outputs": [],
      "source": [
        "features, labels = next(iter(train_dataset))\n",
        "\n",
        "print(features)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E63mArnQaAGz"
      },
      "source": [
        "Обратите внимание, что одинаковые признаки сгруппированы вместе или *пакетированы*. Поля каждой строки добавляются к соответствующему массиву признаков. Измените batch_size, чтобы установить другое количество признаков, хранящихся в этих массивах, ранее мы установили его равным 32.\n",
        "\n",
        "Вы можете увидеть несколько кластеры, построив график признаков из пакета:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "me5Wn-9FcyyO"
      },
      "outputs": [],
      "source": [
        "plt.scatter(features['petal_length'],\n",
        "            features['sepal_length'],\n",
        "            c=labels,\n",
        "            cmap='viridis')\n",
        "\n",
        "plt.xlabel(\"Длина лепестка\")\n",
        "plt.ylabel(\"Длина чашелистика\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YlxpSyHlhT6M"
      },
      "source": [
        "Чтобы упростить процесс построения модели, создайте функцию для перепаковки словаря признаков в единый массив с формой: `(batch_size, num_features)`.\n",
        "\n",
        "Эта функция использует метод tf.stack, который принимает значения из списка тензоров и создает комбинированный тензор указанной размерности:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jm932WINcaGU"
      },
      "outputs": [],
      "source": [
        "def pack_features_vector(features, labels):\n",
        "  \"\"\"Упаковка признаков и одномерный массив.\"\"\"\n",
        "  features = tf.stack(list(features.values()), axis=1)\n",
        "  return features, labels"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "V1Vuph_eDl8x"
      },
      "source": [
        "Затем используйте метод `tf.data.Dataset.map`, чтобы упаковать признаки каждой пары `(features, label)` в обучающий датасет:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZbDkzGZIkpXf"
      },
      "outputs": [],
      "source": [
        "train_dataset = train_dataset.map(pack_features_vector)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NLy0Q1xCldVO"
      },
      "source": [
        "Теперь каждый элемент features в `Dataset` представляет собой массив с размерностью `(batch_size, num_features)`. Давайте посмотрим на первые тензоры:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kex9ibEek6Tr"
      },
      "outputs": [],
      "source": [
        "features, labels = next(iter(train_dataset))\n",
        "\n",
        "print(features[:5])\n",
        "print(labels[:5])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LsaVrtNM3Tx5"
      },
      "source": [
        "## Выбор типа модели\n",
        "\n",
        "### Почему именно модель?\n",
        "\n",
        "*[Модель](https://developers.google.com/machine-learning/crash-course/glossary#model)* - это связь между признаками и меткой. Для задачи классификации цветов ириса модель определяет взаимосвязь между размерами чашелистиков и лепестков и прогнозируемыми видами ирисов. Некоторые простые модели можно описать с помощью нескольких строк алгебры, но сложные модели машинного обучения имеют большое количество параметров, которые сложно обобщать.\n",
        "\n",
        "Смогли бы вы определить взаимосвязь между четырьмя характеристиками цветка и видом ириса *без* машинного обучения? То есть, можете ли вы использовать традиционные методы программирования (например, множество условных операторов) для создания модели? Возможно, если достаточно долго анализировать набор данных, у вас получится определить взаимосвязь между размерами лепестков и чашелистиков для конкретного вида. Но это становится трудным, или даже невозможным для более сложных наборов данных. Хороший подход к машинному обучению *модель определяет за вас*. Если вы загрузите достаточное количество репрезентативных примеров в правильный тип модели машинного обучения, программа сама определит взаимосвязи.\n",
        "\n",
        "### Выбор модели\n",
        "\n",
        "Нам нужно выбрать модель для обучения. Есть множество типов моделей, и чтобы выбрать хорошую, нужен опыт. В этом руководстве для решения проблемы классификации цветков ириса используется нейронная сеть. *[Нейронные сети](https://developers.google.com/machine-learning/glossary/#neural_network)* могут обнаруживать сложные отношения между признаками и меткой. Это хорошо структурированный граф, организованный в один или несколько *[скрытых слоев](https://developers.google.com/machine-learning/glossary/#hidden_layer)*. Каждый скрытый слой состоит из одного или нескольких *[нейронов](https://developers.google.com/machine-learning/glossary/#neuron)*. Существует несколько категорий нейронных сетей, и эта программа использует плотную или *[полносвязанную нейронную сеть](https://developers.google.com/machine-learning/glossary/#fully_connected_layer)*: нейроны следующего слоя получают входные параметры от *каждого* нейрона предыдущего слоя. Например, на рис.2 показана полносвязная нейронная сеть, состоящая из входного слоя, двух скрытых слоев и выходного слоя:\n",
        "\n",
        "<table>\n",
        "  <tr><td>\n",
        "    <img src=\"https://www.tensorflow.org/images/custom_estimators/full_network.png\"\n",
        "         alt=\"A diagram of the network architecture: Inputs, 2 hidden layers, and outputs\">\n",
        "  </td></tr>\n",
        "  <tr><td align=\"center\">\n",
        "    <b>Figure 2.</b> A neural network with features, hidden layers, and predictions.<br/>&nbsp;\n",
        "  </td></tr>\n",
        "</table>\n",
        "\n",
        "Когда модель, изображенная на рис.2, обучается и ей подается немаркированный пример, она дает три прогноза: вероятность того, что этот цветок принадлежит данному виду ириса. Этот прогноз называется *[вывод(inference)](https://developers.google.com/machine-learning/crash-course/glossary#inference)*. В этом примере сумма прогнозов на выходе равна 1.0. На рис.2 этот прогноз разделен на: 0,02 для *Iris setosa*, 0,95 для *Iris versicolor* и 0,03 для *Iris virginica*. Это означает, что модель предсказывает с вероятностью 95%, что пример цветка без метки - это *Iris versicolor*."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W23DIMVPQEBt"
      },
      "source": [
        "### Создание модели с использованиемng Keras\n",
        "\n",
        "API TensorFlow `tf.keras` - предпочтительный способ создания моделей и слоев. Этот способ позволяет легко создавать модели и экспериментировать с ними, в то время как `Keras` справляется со сложностью соединения всего вместе.\n",
        "\n",
        "Модель `tf.keras.Sequential` представляет собой последовательный набор слоев. Его конструктор принимает список экземпляров слоя, в данном случае два слоя `tf.keras.layers.Dense` с 10 узлами в каждом и выходной слой `tf.keras.layers.Dense` с 3 узлами, представляющими наши предсказания(так как у нас всего 3 вида ириса). Параметр `input_shape` первого слоя соответствует количеству объектов из набора данных и является обязательным:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2fZ6oL2ig3ZK"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Dense(10, activation=tf.nn.relu, input_shape=(4,)),  # input shape required\n",
        "  tf.keras.layers.Dense(10, activation=tf.nn.relu),\n",
        "  tf.keras.layers.Dense(3)\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FHcbEzMpxbHL"
      },
      "source": [
        "*[Функция активации](https://developers.google.com/machine-learning/crash-course/glossary#activation_function)* определяет форму вывода каждого узла в слое. Эти функции важны - без них модель была бы эквивалентна одному слою. Существует много `tf.keras.activations`, но [ReLU](https://developers.google.com/machine-learning/crash-course/glossary#ReLU) является обычным для скрытых слоев.\n",
        "\n",
        "Идеальное количество скрытых слоев и нейронов зависит от проблемы и набора данных. Как и многие другие аспекты машинного обучения, выбор наилучшей формы нейронной сети требует сочетания знаний и экспериментов. Как показывает практика, увеличение количества скрытых слоев и нейронов обычно создает более мощную модель, которая требует **большего количества данных** для эффективного обучения."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2wFKnhWCpDSS"
      },
      "source": [
        "### Использование модели\n",
        "\n",
        "Давайте быстро посмотрим, что эта модель делает с рядом признаков:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xe6SQ5NrpB-I"
      },
      "outputs": [],
      "source": [
        "predictions = model(features)\n",
        "predictions[:5]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wxyXOhwVr5S3"
      },
      "source": [
        "Здесь каждый пример возвращает [logit](https://developers.google.com/machine-learning/crash-course/glossary#logits) для каждого класса.\n",
        "\n",
        "Чтобы преобразовать эти немасштабированные значения в вероятность для каждого класса, используйте функцию [softmax](https://developers.google.com/machine-learning/crash-course/glossary#softmax):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_tRwHZmTNTX2"
      },
      "outputs": [],
      "source": [
        "tf.nn.softmax(predictions[:5])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uRZmchElo481"
      },
      "source": [
        "Взяв `tf.argmax` из полученных классов, мы получаем прогнозируемый индекс класса. Но модель еще не была обучена, поэтому прогнозы по большей части неправильные:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-Jzm_GoErz8B"
      },
      "outputs": [],
      "source": [
        "print(\"Прогноз: {}\".format(tf.argmax(predictions, axis=1)))\n",
        "print(\"Реальный класс: {}\".format(labels))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Vzq2E5J2QMtw"
      },
      "source": [
        "## Обучение модели\n",
        "\n",
        "*[Обучение](https://developers.google.com/machine-learning/crash-course/glossary#training)* - это этап машинного обучения, на котором модель постепенно оптимизируется, или, другими словами, модель *изучает* набор данных. Цель обучения состоит в том, чтобы узнать достаточно о структуре обучающих данных и делать прогнозы относительно невидимых данных. Если вы узнаете *слишком много* об обучающем наборе данных, то прогнозы будут работать только для тех данных, которые модель уже видела, и не будут обобщать основные признаки. Эта проблема называется *[переобучение](https://developers.google.com/machine-learning/crash-course/glossary#overfitting)* - это как запоминание ответов, а не понимание того, как решить проблему.\n",
        "\n",
        "Проблема классификации Iris - это пример *[машинного обучения с учителем](https://developers.google.com/machine-learning/glossary/#supervised_machine_learning)*: модель обучается на примерах, содержащих метки. \n",
        "В *[машинном обучение без учителя](https://developers.google.com/machine-learning/glossary/#unsupervised_machine_learning)* примеры не содержат ярлыков. Вместо этого модель пытается находить закономерности среди функций."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RaKp8aEjKX6B"
      },
      "source": [
        "### Определение функции потерь и градиента\n",
        "\n",
        "На этапах обучения и оценки необходимо рассчитать *[потери модели](https://developers.google.com/machine-learning/crash-course/glossary#loss)*. Функции потерь измеряют, насколько прогнозы модели отличаются от желаемой метки, другими словами, насколько плохо модель работает. Мы хотим минимизировать или оптимизировать это значение.\n",
        "\n",
        "Наша модель будет рассчитывать потери с помощью функции `tf.keras.losses.SparseCategoricalCrossentropy`, которая принимает прогнозируемые вероятности класса и реальную метку и возвращает средние потери."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QOsi6b-1CXIn"
      },
      "outputs": [],
      "source": [
        "loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tMAT4DcMPwI-"
      },
      "outputs": [],
      "source": [
        "def loss(model, x, y, training):\n",
        "  # training = training нужно только если есть слои с разными\n",
        "  # поведением во время обучения по сравнению с выводом (например, слой Dropout).\n",
        "  y_ = model(x, training=training)\n",
        "\n",
        "  return loss_object(y_true=y, y_pred=y_)\n",
        "\n",
        "\n",
        "l = loss(model, features, labels, training=False)\n",
        "print(\"Величина потерь: {}\".format(l))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3IcPqA24QM6B"
      },
      "source": [
        "Используйте `tf.GradientTape` для вычисления *[градиентов](https://developers.google.com/machine-learning/crash-course/glossary#gradient)*, используемых для оптимизации вашей модели:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "x57HcKWhKkei"
      },
      "outputs": [],
      "source": [
        "def grad(model, inputs, targets):\n",
        "  with tf.GradientTape() as tape:\n",
        "    loss_value = loss(model, inputs, targets, training=True)\n",
        "  return loss_value, tape.gradient(loss_value, model.trainable_variables)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lOxFimtlKruu"
      },
      "source": [
        "### Создание оптимайзера\n",
        "\n",
        "*[Оптимайзер](https://developers.google.com/machine-learning/crash-course/glossary#optimizer)* применяет вычисленные градиенты к переменным модели, чтобы минимизировать `функцию потерь`. Вы можете представить функцию потерь как изогнутую поверхность (см. Рисунок 3), и мы хотим найти ее самую низкую точку. Градиенты указывают на направление наискорейшего подъема, поэтому мы пойдем в обратном направлении и спустимся вниз. Итеративно вычисляя потери и градиент для каждого пакета данных, мы скорректируем модель во время обучения. Постепенно модель найдет наилучшее сочетание весов и смещения, чтобы минимизировать потери. И чем меньше потери, тем лучше прогнозы модели.\n",
        "\n",
        "<table>\n",
        "  <tr><td>\n",
        "    <img src=\"https://cs231n.github.io/assets/nn3/opt1.gif\" width=\"70%\"\n",
        "         alt=\"Optimization algorithms visualized over time in 3D space.\">\n",
        "  </td></tr>\n",
        "  <tr><td align=\"center\">\n",
        "    <b>Figure 3.</b> Optimization algorithms visualized over time in 3D space.<br/>(Source: <a href=\"http://cs231n.github.io/neural-networks-3/\">Stanford class CS231n</a>, MIT License, Image credit: <a href=\"https://twitter.com/alecrad\">Alec Radford</a>)\n",
        "  </td></tr>\n",
        "</table>\n",
        "\n",
        "В TensorFlow есть множество алгоритмов оптимизации, доступных для обучения. Наша модель использует `tf.keras.optimizers.SGD`, который реализует алгоритм *[стохастический градиентный спуск](https://developers.google.com/machine-learning/crash-course/glossary#gradient_descent)* (SGD). `learning_rate` устанавливает размер шага для каждой итерации спуска. Это *гиперпараметр*, который вы обычно настраиваете для достижения лучших результатов."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XkUd6UiZa_dF"
      },
      "source": [
        "Давайте определим оптимайзер:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8xxi2NNGKwG_"
      },
      "outputs": [],
      "source": [
        "optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pJVRZ0hP52ZB"
      },
      "source": [
        "Используем его для расчета одного шага оптимизации:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rxRNTFVe56RG"
      },
      "outputs": [],
      "source": [
        "loss_value, grads = grad(model, features, labels)\n",
        "\n",
        "print(\"Шаг: {}, Начальная величина потерь: {}\".format(optimizer.iterations.numpy(),\n",
        "                                          loss_value.numpy()))\n",
        "\n",
        "optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
        "\n",
        "print(\"Шаг: {}, Величина потерь: {}\".format(optimizer.iterations.numpy(),\n",
        "                                          loss(model, features, labels, training=True).numpy()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7Y2VSELvwAvW"
      },
      "source": [
        "### Тренировочный цикл\n",
        "\n",
        "Когда все детали на месте, модель готова к обучению! Цикл обучения передает в модель пакеты тренировочных данных, чтобы помочь ей делать более точные прогнозы. Следующий блок кода определяет шаги обучения:\n",
        "\n",
        "1. Итерировать каждую *эпоху*. Эпоха - это один проход по набору данных.\n",
        "2. В течение эпохи перебирать каждый пример в обучающем `Dataset`, захватывая его *признаки* (` x`) и *метку* (`y`).\n",
        "3. Используя признаки набора данных, сделать прогноз и сравнить его с меткой. Измерить ошибочность прогноза и использовать ее для расчета величины потерь и градиентов модели.\n",
        "4. Использовать `optimizer`, чтобы обновить переменные модели.\n",
        "5. Следить за статистикой для визуализации.\n",
        "6. Повторить предыдущие шаги для каждой эпохи.\n",
        "\n",
        "Переменная `num_epochs` - это количество повторений цикла прохода модели по всему датасету. Как ни странно, более длительное обучение не гарантирует получение лучшей модели. `num_epochs` - это *[гиперпараметр](https://developers.google.com/machine-learning/glossary/#hyperparameter)*, который вы можете настраивать. Выбор правильного числа обычно требует как опыта, так и экспериментов:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "AIgulGRUhpto"
      },
      "outputs": [],
      "source": [
        "## Примечание. При повторном запуске этой ячейки используются те же переменные модели.\n",
        "\n",
        "# Сохраним результаты для графиков\n",
        "train_loss_results = []\n",
        "train_accuracy_results = []\n",
        "\n",
        "num_epochs = 201\n",
        "\n",
        "for epoch in range(num_epochs):\n",
        "  epoch_loss_avg = tf.keras.metrics.Mean()\n",
        "  epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()\n",
        "\n",
        "  # Тренировочный цикл - используем пакеты по 32 записи\n",
        "  for x, y in train_dataset:\n",
        "    # Оптимизируем\n",
        "    loss_value, grads = grad(model, x, y)\n",
        "    optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
        "\n",
        "    # Отслеживаем прогресс\n",
        "    epoch_loss_avg.update_state(loss_value)  # Добавляем величину потерь текущего пакета\n",
        "    # Сравниваем спрогнозированную и реальную метки\n",
        "    # training=True нужно только если есть слои с разными\n",
        "    # поведением во время обучения по сравнению с выводом (например, слой Dropout).\n",
        "    epoch_accuracy.update_state(y, model(x, training=True))\n",
        "\n",
        "  # Конец эпохи\n",
        "  train_loss_results.append(epoch_loss_avg.result())\n",
        "  train_accuracy_results.append(epoch_accuracy.result())\n",
        "\n",
        "  if epoch % 50 == 0:\n",
        "    print(\"Эпоха {:03d}: Потери: {:.3f}, Точность: {:.3%}\".format(epoch,\n",
        "                                                                epoch_loss_avg.result(),\n",
        "                                                                epoch_accuracy.result()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2FQHVUnm_rjw"
      },
      "source": [
        "### Визуализация функции потерь"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j3wdbmtLVTyr"
      },
      "source": [
        "Хотя полезно выводить прогресс обучения модели, часто бывает полезнее увидеть графическое представление этого прогресса. [TensorBoard](https://www.tensorflow.org/tensorboard) - хороший инструмент визуализации, который входит в состав TensorFlow, но мы можем создавать базовые диаграммы с помощью модуля matplotlib.\n",
        "\n",
        "Интерпретация этих графиков требует некоторого опыта, но вы должны стремится к тому, чтобы потери уменьшались, а точность повышалась:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "agjvNd2iUGFn"
      },
      "outputs": [],
      "source": [
        "fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))\n",
        "fig.suptitle('Training Metrics')\n",
        "\n",
        "axes[0].set_ylabel(\"Loss\", fontsize=14)\n",
        "axes[0].plot(train_loss_results)\n",
        "\n",
        "axes[1].set_ylabel(\"Accuracy\", fontsize=14)\n",
        "axes[1].set_xlabel(\"Epoch\", fontsize=14)\n",
        "axes[1].plot(train_accuracy_results)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Zg8GoMZhLpGH"
      },
      "source": [
        "## Оценка эффективности модели\n",
        "\n",
        "Теперь, когда модель обучена, мы можем получить некоторую статистику о ее производительности.\n",
        "\n",
        "*Оценка модели* означает определение того, насколько эффективно модель делает прогнозы. Чтобы определить эффективность модели при классификации цветков ириса, передайте модели некоторые величины чашелистиков и лепестков и попросите модель предсказать, какие виды ирисов они представляют. Затем сравните прогнозы модели с фактическими данными. Например, модель, которая выбрала правильный вид в половине примеров входных данных, имеет *[точность](https://developers.google.com/machine-learning/glossary/#accuracy)* `0.5`. На рисунке 4 показана более эффективная модель, в которой 4 из 5 прогнозов верны и точность составляет 80%:\n",
        "\n",
        "<table cellpadding=\"8\" border=\"0\">\n",
        "  <colgroup>\n",
        "    <col span=\"4\" >\n",
        "    <col span=\"1\" bgcolor=\"lightblue\">\n",
        "    <col span=\"1\" bgcolor=\"lightgreen\">\n",
        "  </colgroup>\n",
        "  <tr bgcolor=\"lightgray\">\n",
        "    <th colspan=\"4\">Example features</th>\n",
        "    <th colspan=\"1\">Label</th>\n",
        "    <th colspan=\"1\" >Model prediction</th>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>5.9</td><td>3.0</td><td>4.3</td><td>1.5</td><td align=\"center\">1</td><td align=\"center\">1</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>6.9</td><td>3.1</td><td>5.4</td><td>2.1</td><td align=\"center\">2</td><td align=\"center\">2</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>5.1</td><td>3.3</td><td>1.7</td><td>0.5</td><td align=\"center\">0</td><td align=\"center\">0</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>6.0</td> <td>3.4</td> <td>4.5</td> <td>1.6</td> <td align=\"center\">1</td><td align=\"center\" bgcolor=\"red\">2</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>5.5</td><td>2.5</td><td>4.0</td><td>1.3</td><td align=\"center\">1</td><td align=\"center\">1</td>\n",
        "  </tr>\n",
        "  <tr><td align=\"center\" colspan=\"6\">\n",
        "    <b>Figure 4.</b> An Iris classifier that is 80% accurate.<br/>&nbsp;\n",
        "  </td></tr>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z-EvK7hGL0d8"
      },
      "source": [
        "### Тестовый датасет\n",
        "\n",
        "Оценка модели аналогична обучению модели. Основная разница в том, что примеры взяты из отдельного *[тестового датасета](https://developers.google.com/machine-learning/crash-course/glossary#test_set)*, а не из датасета для обучения. Чтобы достоверно оценить эффективность модели, примеры, используемые для оценки модели, должны отличаться от примеров, используемых для обучения модели.\n",
        "\n",
        "Настройка тестового `Dataset` аналогична настройке тренировочного `Dataset`. Загрузите текстовый файл CSV и прочитайте из него данные, а затем немного перемешайте:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ps3_9dJ3Lodk"
      },
      "outputs": [],
      "source": [
        "test_url = \"https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\"\n",
        "\n",
        "test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url),\n",
        "                                  origin=test_url)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SRMWCu30bnxH"
      },
      "outputs": [],
      "source": [
        "test_dataset = tf.data.experimental.make_csv_dataset(\n",
        "    test_fp,\n",
        "    batch_size,\n",
        "    column_names=column_names,\n",
        "    label_name='species',\n",
        "    num_epochs=1,\n",
        "    shuffle=False)\n",
        "\n",
        "test_dataset = test_dataset.map(pack_features_vector)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HFuOKXJdMAdm"
      },
      "source": [
        "### Оценка модели на тестовом датасете\n",
        "\n",
        "В отличие от этапа обучения, модель оценивается только одной [эпохой](https://developers.google.com/machine-learning/glossary/#epoch) тестовых данных. В следующих строках кода мы перебираем каждый пример в тестовом датасете и сравниваем прогноз модели с фактической меткой. Этот метод используется для измерения точности модели по всему набору тестов:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Tw03-MK1cYId"
      },
      "outputs": [],
      "source": [
        "test_accuracy = tf.keras.metrics.Accuracy()\n",
        "\n",
        "for (x, y) in test_dataset:\n",
        "  logits = model(x, training=False)\n",
        "  prediction = tf.argmax(logits, axis=1, output_type=tf.int32)\n",
        "  test_accuracy(prediction, y)\n",
        "\n",
        "print(\"Точность на тестовых данных: {:.3%}\".format(test_accuracy.result()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HcKEZMtCOeK-"
      },
      "source": [
        "Мы можем просмотреть последний пакет и убедится, что в большем количесвте случаев модель прогнозирует правильно:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uNwt2eMeOane"
      },
      "outputs": [],
      "source": [
        "tf.stack([y,prediction],axis=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7Li2r1tYvW7S"
      },
      "source": [
        "## Использование обученной модели\n",
        "\n",
        "Мы обучили модель и «доказали», что она хороша(но не идеальна) в классификации видов ирисов. Теперь давайте воспользуемся обученной моделью, чтобы сделать некоторые прогнозы для [немаркированных примеров](https://developers.google.com/machine-learning/glossary/#unlabeled_example); то есть примеры, которые содержат признаки, но не содержат метку.\n",
        "\n",
        "В реальной жизни призанки без меток могут быть получены из множества различных источников, включая приложения, файлы CSV или каналы данных. А пока мы попробуем загрузить в модель три немаркированных примера, чтобы предсказать их метки. Напомним, номера меток отображаются в именованное представление как:\n",
        "\n",
        "* `0`: Iris setosa\n",
        "* `1`: Iris versicolor\n",
        "* `2`: Iris virginica"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kesTS5Lzv-M2"
      },
      "outputs": [],
      "source": [
        "predict_dataset = tf.convert_to_tensor([\n",
        "    [5.1, 3.3, 1.7, 0.5,],\n",
        "    [5.9, 3.0, 4.2, 1.5,],\n",
        "    [6.9, 3.1, 5.4, 2.1]\n",
        "])\n",
        "\n",
        "# training=False нужно только если есть слои с разными\n",
        "# поведением во время обучения по сравнению с выводом (например, слой Dropout).\n",
        "predictions = model(predict_dataset, training=False)\n",
        "\n",
        "for i, logits in enumerate(predictions):\n",
        "  class_idx = tf.argmax(logits).numpy()\n",
        "  p = tf.nn.softmax(logits)[class_idx]\n",
        "  name = class_names[class_idx]\n",
        "  print(\"Example {} prediction: {} ({:4.1f}%)\".format(i, name, 100*p))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "custom_training_walkthrough.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
